package tksTcp

import (
	"bytes"
	"encoding/binary"
	"net"
)

const (
	message_header_len   = 14
	message_body_max_len = 1024 + message_header_len
)

type NewConnCb func(conn net.Conn)
type MsgCb func(conn net.Conn, from uint32, to uint32, seq uint16, cmd uint16, data []byte)
type ConnCloseCb func(conn net.Conn)

type TksTcpModule struct {
	Conn net.Conn
}

func NewServer(port string, nc NewConnCb, msg MsgCb, cc ConnCloseCb) (*TksTcpModule, error) {
	server := new(TksTcpModule)
	listener, err := net.Listen("tcp", port)
	if err != nil {
		return nil, err
	}
	for {
		conn, err1 := listener.Accept()
		if err1 != nil {
			return nil, err1
		}
		if nc != nil {
			nc(conn)
		}

		//
		go ReceiveBuffer(conn, msg, cc)
	}
	return server, nil
}

func NewClient(host string, msg MsgCb, cc ConnCloseCb) (*TksTcpModule, error) {
	client := new(TksTcpModule)
	conn, err := net.Dial("tcp", host)
	client.Conn = conn
	if err != nil {
		return nil, err
	}
	//
	go ReceiveBuffer(conn, msg, cc)
	return client, nil
}

func SendBuffer(conn net.Conn, from uint32, to uint32, seq uint16, cmd uint16, buffer []byte) {
	if conn == nil {
		return
	}
	// [4:from][4:to][2:seq][2:cmd][2:len]
	buf := make([]byte, len(buffer)+message_header_len)
	binary.BigEndian.PutUint32(buf[0:4], from)
	binary.BigEndian.PutUint32(buf[4:8], to)
	binary.BigEndian.PutUint16(buf[8:10], seq)
	binary.BigEndian.PutUint16(buf[10:12], cmd)
	binary.BigEndian.PutUint16(buf[12:14], uint16(len(buffer)))
	copy(buf[14:], buffer)
	conn.Write(buf)
}

func ReceiveBuffer(conn net.Conn, msg MsgCb, cc ConnCloseCb) {
	cache := make([]byte, message_body_max_len)
	buf := bytes.NewBuffer(make([]byte, 0, message_body_max_len))
	var len uint16
	var seq uint16
	var cmd uint16
	var from uint32
	var to uint32
	for {
		size, err := conn.Read(cache)
		if err != nil {
			cc(conn)
			return
		}
		buf.Write(cache[:size])
		for {
			if buf.Len() < message_header_len {
				break
			}
			if len == 0 {
				i32 := make([]byte, 4)
				buf.Read(i32)
				from = binary.BigEndian.Uint32(i32)
				buf.Read(i32)
				to = binary.BigEndian.Uint32(i32)
				i16 := make([]byte, 2)
				buf.Read(i16)
				seq = binary.BigEndian.Uint16(i16)
				buf.Read(i16)
				cmd = binary.BigEndian.Uint16(i16)
				buf.Read(i16)
				len = binary.BigEndian.Uint16(i16)
			}
			if int(len) > buf.Len() || len == 0 {
				break
			}
			data := make([]byte, len)
			_, err = buf.Read(data)

			msg(conn, from, to, seq, cmd, data)
			len = 0
		}
	}
}
